package xyz.jcat.mongo;

import cn.craccd.mongoHelper.utils.MongoHelper;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import xyz.jcat.common.exception.DBException;
import xyz.jcat.common.util.CollectionUtils;

import java.util.Date;
import java.util.List;
import java.util.Objects;

public class NumberIdService<D extends NumberIdDocument, ID extends Number> implements IService<D, ID> {

    protected MongoHelper dao;
    protected MongoTemplate mongoTemplate;
    @Autowired
    private MongoLeafIdWorker<ID> idWorker;

    @Override
    public D insert(D d) {
        setBaseValues(d);
        return mongoTemplate.insert(d);
    }

    @Override
    public void insertBatch(List<D> documents) {
        if(CollectionUtils.isEmpty(documents)) {
            return;
        }
        documents.forEach(document -> {
            setBaseValues(document);
        });
        mongoTemplate.insertAll(documents);
    }

    private void setBaseValues(D d) {
        if(Objects.isNull(d.getId())) {
            d.setId(idWorker.nextId());
        }
        if(Objects.isNull(d.getCreateTime())) {
            d.setCreateTime(new Date());
        }
        if(Objects.isNull(d.getUpdateTime())) {
            d.setUpdateTime(d.getCreateTime());
        }
    }

    @Override
    public D updateById(D d) {
        if(Objects.isNull(d.getId())) {
            throw new DBException("id is required");
        }
        if(Objects.isNull(d.getUpdateTime())) {
            d.setUpdateTime(new Date());
        }
        return mongoTemplate.save(d);
    }

    @Override
    public D getById(ID id, Class<D> clazz) {
        return mongoTemplate.findById(id, clazz);
    }

    @Override
    public boolean deleteById(ID id, Class<D> clazz) {
        DeleteResult deleteResult = mongoTemplate.remove(getQueryWhereId(id), clazz);
        return deleteResult.wasAcknowledged() && deleteResult.getDeletedCount() == 1;
    }

    @Override
    public boolean inc(ID id, String field, Class<D> clazz) {
        return inc(id, field, 1L, clazz);
    }

    @Override
    public boolean inc(ID id, String field, Long count, Class<D> clazz) {
        return inc(id, field, count, true, clazz);
    }

    @Override
    public boolean inc(ID id, String field, Long count, boolean isPositiveField, Class<D> clazz) {
        Update update = new Update();
        update.inc(field, count);
        Query query = getQueryWhereId(id);
        if(isPositiveField) {
            query.addCriteria(Criteria.where(field).gte(-count));
        }
        UpdateResult updateResult = mongoTemplate.updateFirst(query, update, clazz);
        return updateResult.wasAcknowledged() && updateResult.getModifiedCount() == 1;
    }

    @Autowired
    public void setDao(MongoHelper mongoHelper) {
        this.dao = mongoHelper;
        this.mongoTemplate = mongoHelper.getMongoTemplate();
    }

    private  Query getQueryWhereId(ID id) {
        return new Query(Criteria.where(NumberIdDocument.field_id).is(id));
    }
}
